#pragma once

// #include "oj_model.hpp"
#include "oj_model2.hpp"
#include "oj_view.hpp"
#include "../comm/httplib.h"

#include <vector>
#include <mutex>
#include <memory>
#include <fstream>
#include <algorithm>
#include <jsoncpp/json/json.h>

namespace ns_control
{
    using namespace ns_model;
    using namespace ns_view;
    using namespace httplib;

    // 服务主机
    struct Machine
    {
        std::string ip;   // 主机IP
        int port;         // 编译服务的port
        uint64_t load;    // 编译服务的负载
        std::mutex mutex; // 保证load的线程安全

        Machine() {}
        Machine(std::string cip, int cport) : ip(cip), port(cport), load(0) {}
        ~Machine() {}

        // 获取负载值
        uint64_t Load()
        {
            std::unique_lock<std::mutex> _lock(mutex);
            return load;
        }

        void reset()
        {
            std::unique_lock<std::mutex> _lock(mutex);
            load = 0;
        }

        // 提高主机负载
        void IncLoad()
        {
            std::unique_lock<std::mutex> _lock(mutex);
            ++load;
        }
        // 降低主机负载
        void DecLoad()
        {
            std::unique_lock<std::mutex> _lock(mutex);
            --load;
        }
    };

    // 负载均衡模块
    using MachinePtr = std::shared_ptr<Machine>;
    const std::string service_machine = "./conf/service_machine.conf";
    class LoadBlance
    {
    private:
        // 管理所有提供编译服务的主机,每个主机都有一个唯一的下标 ,我们把下标作为主机的唯一标识存放进online和offline中
        std::vector<MachinePtr> _machines;
        std::vector<int> online;
        std::vector<int> offline;
        std::mutex _mutex;

    public:
        LoadBlance()
        {
            assert(LoadConf(service_machine));
            LOG(INFO) << "加载 " << service_machine << " 成功,当前在线主机数量 : " << online.size() << "\n";
        }

    private:
        bool LoadConf(const std::string &machine_conf)
        {
            std::fstream in(machine_conf);
            if (!in.is_open())
            {
                LOG(FATAL) << " 加载: " << machine_conf << " 失败" << "\n";
                return false;
            }

            std::string line;
            while (std::getline(in, line))
            {
                std::vector<std::string> tokens;
                StringUtil::Split(line, ":", &tokens);
                if (tokens.size() != 2)
                {
                    LOG(WARNING) << "加载部分服务主机失败，请检查配置文件格式" << "\n";
                    continue;
                }

                // 构建主机
                MachinePtr mp = std::make_shared<Machine>(tokens[0], std::stoi(tokens[1]));
                online.push_back(_machines.size());
                _machines.push_back(mp);
            }

            in.close();
            return true;
        }

    public:
        bool SmartChoice(int *id, MachinePtr *m)
        {
            {
                // 获取在线主机数量
                std::unique_lock<std::mutex> lock(_mutex);
                int online_num = online.size();
                if (online_num == 0)
                {
                    LOG(FATAL) << "后端编译主机已经全部离线!!!, 请尽快修复" << "\n";
                    return false;
                }

                // 遍历在线主机,寻找负载最低的
                uint64_t min_load = _machines[online[0]]->Load();
                int min_index = online[0]; // 存放最小负载主机的下标
                for (int i = 1; i < online_num; i++)
                {
                    if (min_load > _machines[i]->Load())
                    {
                        min_load = _machines[i]->Load();
                        min_index = online[i];
                    }
                }

                // 将最小负载主机的ID和对象返回给外界
                *id = min_index;
                *m = _machines[min_index];
                return true;
            }
        }

        // 指定主机离线
        void OfflineMachine(int id)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            for (auto iter = online.begin(); iter != online.end(); ++iter)
            {
                if (*iter == id)
                {
                    // 找到要离线的主机了
                    offline.push_back(id);
                    online.erase(iter);
                    break;
                }
            }
        }

        // 全部主机上线
        void onlineMachines()
        {
            std::unique_lock<std::mutex> lock(_mutex);

            if (online.size() != 0)
            {
                LOG(WARNING) << "当前还有主机在线!!!" << '\n';
                return;
            }

            // 所有主机全部离线
            std::swap(online, offline);
            LOG(INFO) << "所有的主机上线啦" << '\n';
        }
    };

    // 这里核心业务功能
    class Control
    {
    private:
        Model _model;            // 提供后台数据
        View _view;              // 提供html渲染功能
        LoadBlance _load_blance; // 核心负载均衡器
    public:
        Control() {}
        ~Control() {}

    public:
    //上线所有主机,只有当主机全部离线才可以使用
        void RecoveryMachine()
        {
            _load_blance.onlineMachines();
        }

        bool AllQuestions(std::string *html)
        {
            bool ret = true;
            std::vector<Question> questions;

            if (_model.GetAllQuestions(&questions))
            {
                // 按照题号排升序
                sort(questions.begin(), questions.end(), [](const Question &q1, const Question &q2)
                     { return q1.number < q2.number; });
                // 获取题目信息成功，将所有的题目数据构建成网页
                _view.AllExpandHtml(questions, html);
            }
            else
            {
                *html = "获取题目失败, 形成题目列表失败";
                ret = false;
            }

            return ret;
        }
        bool OneQuestion(const std::string &number, std::string *html)
        {
            bool ret = true;
            Question q;
            if (_model.GetOneQuestions(number, &q))
            {
                // 获取指定题目信息成功，将指定题目数据构建成网页
                _view.OneExpandHtml(q, html);
            }
            else
            {
                *html = "指定题目: " + number + " 不存在!";
                ret = false;
            }

            return ret;
        }

        void Judge(const std::string number, const std::string in_json, std::string *out_string)
        {
            // 0.根据题目编号，获取指定题目
            Question q;
            if (!_model.GetOneQuestions(number, &q))
            {
                return;
            }

            // 1.对in_json反序列化，获取用户提交代码
            Json::Value value;
            Json::Reader reader;
            reader.parse(in_json, value);
            std::string code = value["code"].asString();

            // 2.将测试用例代码和用户提交的代码拼接
            Json::Value compile_value;
            compile_value["code"] = code + '\n' + q.tail;
            compile_value["input"] = value["input"].asString();
            compile_value["cpu_limit"] = q.cpu_limit;
            compile_value["mem_limit"] = q.mem_limit;
            Json::FastWriter writer;
            std::string compile_string = writer.write(compile_value);

            // 3.选择负载最低的主机
            while (true)
            {
                int id; // 主机的id
                MachinePtr mp;
                if (!_load_blance.SmartChoice(&id, &mp))
                {
                    // 主机全部离线的情况
                    break;
                }
                // 4.向编译服务器发送请求
                Client cli(mp->ip, mp->port);
                mp->IncLoad(); // 提交负载
                LOG(INFO) << " 选择主机成功, 主机id: " << id << " 详情: " << mp->ip << ":" << mp->port << " 当前主机的负载是: " << mp->Load() << "\n";
                auto res = cli.Post("/compile_and_run", compile_string, "application/json;charset=utf-8");
                if (res != nullptr)
                {
                    // 请求编译服务成功
                    if (res->status == 200)
                    {
                        // 5.将结果赋值给out_string
                        LOG(INFO) << "请求编译和运行服务成功..." << "\n";
                        *out_string = res->body;
                        mp->DecLoad(); // 降低负载
                        break;
                    }
                    mp->DecLoad();
                }
                else
                {
                    // 当前选择的主机离线的情况
                    LOG(ERROR) << "主机编号: " << id << ", 可能已经离线!!!" << "\n";
                    mp->reset(); // 清空主机的负载
                    _load_blance.OfflineMachine(id);
                }
            }
        }
    };
}